package com.cskaoyan.javase.object._3equals._0introduction;

/**
 * equals方法是Object类当中最为重要的一个方法,非常常用,且用途很多.
 * 1.equals方法的声明: public boolean equals(Object obj)
 *      仅需要注意两点：
 *      a.该方法是有参数的，需要传入一个对象（任意一个对象就行）
 *      b.方法是有返回值的，返回一个布尔类型的值，真或假。往往都是配合if使用
 *
 * 2.equals方法的作用:
 *      比较方法的调用者对象和方法传入的对象,是否相等.
 *      判断两个对象是否相等.
 *      既然是判断对象相等,那么怎么样的两个对象才相等呢?
 *      对象相等的规则是什么呢?
 *      就是equals方法的实现,该方法的实现就是比较对象相等的规则
 *      如果对象相等,就返回true,否者返回false
 *
 * 3.Object类当中equals方法的默认实现(默认决定对象相等的规则):
 *      return (this == obj);
 *      直接根据引用存储的地址判断对象相等,也就是说只有当两个引用完全指向同一个对象时,对象才相等!
 *      这个比较的条件是很苛刻的,实际开发中往往不需要这么苛刻的相等条件.
 *
 * 4.我们认为的对象相等是什么样的?
 *      1.如果两个对象不是同种类型对象,就没有可比性,直接认为是不相等的
 *      2.如果两个对象是同种类型对象,那么就具有可比性
 *          这时只要两个对象的属性取值相等,就可以认为对象相等.
 *
 * 5.equals方法的重写
 *      对于以上比较规则的equals方法的重写，Java官方为我们提供了官方的要求
 *      称之为equals方法重写的常规协定：
 *      a.自反性：对于任何非空引用值 x，x.equals(x)都应返回 true
 *          自己和自己比较,肯定返回true
 *
 *      b.排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *
 *      c.对称性：对于任何非空引用值 x 和 y，当且仅当y.equals(x) 返回 true 时，x.equals(y) 才应返回true
 *      x 和 y 比较
 *      和
 *      y 和 x 比较
 *      结果是一样的
 *
 *      d.传递性：对于任何非空引用值 x、y 和 z，如果x.equals(y)返回 true，并且 y.equals(z) 返回 true，那么x.equals(z) 应返回 true。
 *      x == y
 *      y == z
 *      -->
 *      x == z
 *
 *      e.一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y)始终返回 true 或始终返回 false。(除非某个对象比较的条件发生了变化)
 *
 *      实际上只要用对象的属性取值来判断对象相等
 *      就能够自动满足以上对称性,传递性,一致性
 *
 *
 * 注意事项:
 *      1.两种排它性,getClass更严格只限同种数据类型, instanceof更宽松可以传入子类对象,只要子类和父类成员变量取值一样就会返回true.
 *      2.equals方法格式很固定,实际上也可以用快捷键alt + insert生成它.
 *      3.实际开发中,比较两个浮点数的大小,不建议使用"=="号
 *          虽然"=="在绝大多数情况下(正常程序下)都是没有问题的,可以用来比较两个浮点数的大小
 *          但是浮点数有非规格化的浮点数,无穷大,无穷小,以及非数等等非正常数值概念
 *          这时用"=="是比较不了的
 *          所以从代码严谨的角度考虑,判断浮点数的大小用"=="是不好的选择
 *          推荐使用包装类型.compare(对应类型变量1,对应类型变量2);
 *          包装类型是把一个基本数据类型变量封装成对象
 *          double --> Double(类)
 *          float  --> Float(类)
 *          方法就是
 *          Double.compare(double d1,double d2);
 *          该方法的返回值一个int整数
 *          实际用这个方法时,可以把该方法想象成"d1 - d2"
 *          当方法返回负数时,说明d1 < d2
 *          当方法返回正数时,说明d1 > d2
 *          当方法返回0时,说明d1 = d2
 *
 *     4.如果类中有引用数据类型成员变量，需要去调用它们的equals方法完成比较。这就意味着还需要重写这个类的equals方法。
 * 财务金额上的运算是不推荐使用浮点数的，会出现精度问题。推荐使用BigDecimal这个类完成运算
 *
 * @since 11:02
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(100, 18);
        Student s2 = new Student(100, 18);
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
}

// class Star{}
class Student {
    double score;
    int age;
    String name;
    // Star s;

    public Student(double score, int age) {
        this.score = score;
        this.age = age;
    }

    /*@Override
    public boolean equals(Object obj) {
        // 1.满足自反性
        if (this == obj) {
            return true;
        }

        // 2.满足排它性,标准固定格式
        *//*
            这里用getClass方法实现排它性
            是非常严格的,只要不是完全相同的数据类型
            一律返回false
         *//*
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        // 3.只要用对象的属性取值来判断对象相等
        // 就能够自动满足对称性,传递性,一致性
        // 代码能够运行到这里,说明obj对象一定是Student对象,所以直接做强转,无需判断
        Student target = (Student) obj;
        if (this.age != target.age) {
            return false;
        }
        // 代码运行到这里,age一定相等了,所以score比较的结果就是对象相等的结果,无需if判断了
        return this.score == target.score;
    }*/

    /*@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (Double.compare(student.score, score) != 0) return false;
        return age == student.age;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(score);
        result = (int) (temp ^ (temp >>> 32));
        result = 31 * result + age;
        return result;
    }*/

    /*@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (Double.compare(student.score, score) != 0) return false;
        if (age != student.age) return false;
        return name.equals(student.name);
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(score);
        result = (int) (temp ^ (temp >>> 32));
        result = 31 * result + age;
        result = 31 * result + name.hashCode();
        return result;
    }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (Double.compare(student.score, score) != 0) return false;
        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(score);
        result = (int) (temp ^ (temp >>> 32));
        result = 31 * result + age;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}